#include "CsvFileReader.h"

#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>

#include <iostream>
#include <sstream>

CsvFileReader::CsvFileReader( const std::string& separator, const std::string& inputFilePath )
: m_separator   ( separator ),
  m_csvFile     ( inputFilePath ),
  m_nameDataMap (),
  m_datas       ()
{
    if( !m_csvFile.is_open() )
    {
        m_csvFile.close();
        throw std::invalid_argument( __FUNCTION__ " open csv file failed: " + inputFilePath );
    }

    ParseFirstLine();
}

CsvFileReader::~CsvFileReader( )
{
    m_csvFile.close();
}

bool CsvFileReader::ReadRow( )
{
    std::string line;

    // ignore empty line
    do
    {
        if( ReadLine( line ) )
            boost::trim( line );
        else
            return false;
    }while ( line.size() == 0 );

    std::vector<std::string> datas;
    boost::algorithm::split( datas, line, boost::is_any_of( m_separator ) );

    if( datas.size() != 0 && datas.size() != m_datas.size() )
        throw std::runtime_error( __FUNCTION__ " number of data does not match number of field" );

    unsigned int count = 0;

    for( std::vector<std::string>::iterator inItr = datas.begin(), itr = m_datas.begin();
         inItr != datas.end() &&
         itr != m_datas.end();
         ++inItr, ++itr )
    {
        *itr = *inItr;
    }

    return true;
}

std::string CsvFileReader::GetStringData( const std::string& fieldName )
{
    std::string trimedFieldName = fieldName;
    boost::trim( trimedFieldName );

    std::string rtn = "";
    std::map<std::string, unsigned int>::const_iterator itr = m_nameDataMap.find( trimedFieldName );

    if( itr == m_nameDataMap.end() )
        throw std::runtime_error( __FUNCTION__ " field name " + trimedFieldName + " not found" );

    return m_datas[itr->second];
}

double CsvFileReader::GetDoubleData( const std::string& fieldName )
{
    std::string dataString = GetStringData( fieldName );
    std::istringstream iss(dataString);
    double rtn;
    if (!( iss >> rtn ) )
      throw std::runtime_error( __FUNCTION__ " data of " + fieldName + " is not a numeric" );

    return rtn;
}

void CsvFileReader::ParseFirstLine()
{
    std::string firstLine;

    if( !ReadLine( firstLine ) )
        throw std::invalid_argument( __FUNCTION__ " read first line error" );

    std::vector<std::string> fieldNames;
    boost::algorithm::split( fieldNames, firstLine, boost::is_any_of( m_separator ) );

    unsigned int count = 0;
    for( std::vector<std::string>::iterator it = fieldNames.begin();
         it != fieldNames.end(); ++it )
    {
        boost::trim( *it );
        m_nameDataMap[*it] = count;

        m_datas.push_back( "" );

        ++count;
    }
}

bool CsvFileReader::ReadLine( std::string& line )
{
    if( m_csvFile.eof() )
        return false;

    std::getline(m_csvFile,line);
    return true;
}